SOCKSプロキシ経由でリモートサーバへSFTP接続を行う
はじめに
データアナリティクス事業本部のkobayashiです。
SFTP転送でリモートサーバからデータを取得する必要がありました。Pythonでその様な処理を行おうとするといくつかモジュールの選択肢がありますが今回はParamikoを利用しました。通常の環境ですとあとはPythonのスクリプトでParamikoを呼んで利用すればいだけなのですが、リモートサーバへの接続が接続元IPを制限されている環境で且つSOCKSプロキシを通して接続する必要があったのでその方法をまとめます。
接続元IPを制限された環境へのParamikoでのアクセス
Paramikoを使ってリモートサーバへSFTP接続を試みます。
import paramiko with paramiko.SSHClient() as client: client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect("target_hostname", port=22, username="username", password="password", timeout=30) sftp_connection = client.open_sftp() dirs = sftp_connection.listdir('/') print(dirs)
結果
当然の結果ながら接続に失敗してしまいます。
Traceback (most recent call last): File "/Users/koba/sockes.py", line 22, in <module> client.connect("target_hostname", port=22, username="bisnextsftpt11", password="h@MesTee1", timeout=30) File "/Users/koba/.pyenv/versions/3.7/lib/python3.7/site-packages/paramiko/client.py", line 416, in connect self, server_hostkey_name, server_key File "/Users/koba/.pyenv/versions/3.7/lib/python3.7/site-packages/paramiko/client.py", line 824, in missing_host_key "Server {!r} not found in known_hosts".format(hostname) paramiko.ssh_exception.SSHException: Server 'target_hostname' not found in known_hosts
Pysocks
色々情報を漁ったところPySocksが要件を満たすことがわかりましたのでPySocksを使ってSOCKSプロキシ経由で接続する設定を行います。
PySocks lets you send traffic through SOCKS and HTTP proxy servers. It is a modern fork of SocksiPy with bug fixes and extra features.
Acts as a drop-in replacement to the socket module. Seamlessly configure SOCKS proxies for any socket object by calling socket_object.set_proxy().
PySocksのインストール
pip install PySocks
では具体的にどの様に使うのかというとPyPiのPySocksページ最下部に記述があリます。
In addition to the socksocket class, an additional function worth mentioning is the set_default_proxy function. The parameters are the same as the set_proxy method. This function will set default proxy settings for newly created socksocket objects, in which the proxy settings haven't been changed via the set_proxy method. This is quite useful if you wish to force 3rd party modules to use a SOCKS proxy, by overriding the socket object.
>>> socks.set_default_proxy(socks.SOCKS5, "socks.example.com") >>> socket.socket = socks.socksocket >>> urllib.urlopen("http://www.sourceforge.net/")
設定の仕方は前述のスクリプトを以下の様に修正すればSocksプロキシ経由で接続できるようになります。
import paramiko import socks import socket socks.set_default_proxy(socks.SOCKS5, 'proxy_hostname', 1080) socket.socket = socks.socksocket with paramiko.SSHClient() as client: client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect("target_hostname", port=22, username="username", password="password", timeout=30) sftp_connection = client.open_sftp() dirs = sftp_connection.listdir('/') print(dirs)
結果
['bin', 'dev', 'home', 'opt', 'root', 'sys', 'usr', 'boot', 'etc', 'lib', 'mnt', 'proc', 'sbin', 'tmp', 'var']
上記の修正箇所を解説すると
- PySocksモジュールとsocketをインポートする。
- Proxyサーバの情報を引数として
socks.set_default_proxy()
に渡してsocks.socksocketオブジェクトを生成する。 - socketオブジェクトを2で生成したsocks.socksocketで上書きする。
これでParamikoがPySocksで生成したソケットを通して接続を行えるようになり、接続元をSOCKSプロキシとしてリモートサーバへ接続できる様になります。
他のモジュールでの利用
socketオブジェクトを上書いているのでこれを使っている他のモジュールでもSOCKSプロキシ経由でのリモートサーバへのアクセスができるようになります。
import urllib.request import socks import socket socks.set_default_proxy(socks.SOCKS5, 'proxy_hostname', 1080) socket.socket = socks.socksocket with urllib.request.urlopen("https://dev.classmethod.jp/") as res: print(res.read())
結果
b'<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head><meta content="\x90\xa2\x8aE\x92\x86\x82\xcc\x82\xa0\x82\xe7\x82\xe4\x82\xe9\x8f\xee\x95\xf1\x82\xf0\x8c\x9f\x8d\xf5\x82\xb7\x82\xe9\x82\xbd\x82\xdf\x82 ....
まとめ
リモートで業務を行う際には接続元を制限された環境にアクセスするためにひと手間加える必要があります。セキュリティを考えれば当然ですのでその対応方法をきちんと抑えておく事が大切です。
最後まで読んで頂いてありがとうございました。